'''
pygeom -- Python Geometric Engine
line -- Definition of the Line and related GEs

The line is a GE that consists of two points that are not coincident.
It has, among other things, "heading", the unit pseudovector between its two
points, taken in such a way to be deterministic. (Remember, the pseudovector
means that it is more of an axis.)
'''

from pygeom import ge
from pygeom import cls
from pygeom import constraint
from pygeom import point
from pygeom import matrix

class Line(ge.GE):
    def __init__(self, a, b):
        ge.GE.__init__(self)
        cls.Raise(a.Class, cls.POINT)
        cls.Raise(b.Class, cls.POINT)
        self._cls=(cls.LINE,)
        if a==b:
            raise ValueError('Two unique points required to create Line')
        self.a=a
        self.b=b
    @classmethod
    def FromPointVector(kls, pt, vec, *args, **kwargs):
        return kls(pt,
                   point.Point.FromVector(pt.Vector+vec),
                   *args, **kwargs)
    def __repr__(self):
        return 'Line(%r, %r)'%(self.a, self.b)
    @property
    def Heading(self):
        return (self.a.Vector - self.b.Vector).Unit
    def ClosestPoint(self, pt):
        return self.Intersect(self.MakePerpendicular(pt))
    def ClosestPoints(self, other):
        #You following me here?
        if self.Heading.Cross(other.Heading).Length == 0:
            return None, None
        return (self.a.Vector+self.Heading*(matrix.Matrix(other.a.Vector-self.a.Vector,
                                                          other.Heading,
                                                          self.Heading.Cross(other.Heading)).Determinant/
                                            (self.Heading.Cross(other.Heading).Length)**2),
                other.a.Vector+other.Heading*(matrix.Matrix(other.a.Vector-self.a.Vector,
                                                            self.Heading,
                                                            self.Heading.Cross(other.Heading)).Determinant/
                                              (self.Heading.Cross(other.Heading).Length)**2))
    #Overrides
    def _IsctSetup(self):
        self._IsctMode(intersect.DEFER_ALL|
                       intersect.CLS_LINE|
                       intersect.CLS_POINT)
    def _IsctPoint(self, ge):
        if ge in self:
            return ge
        return None
    def _IsctLine(self, ge):
        #We use a 3x3 Matrix to calculate the closest points on each line.
        #Then we use membership testing to be sure the points are in both
        #lines. (Note: You can retrieve the ClosestPoints via the obviously
        #named function.) An alternate, probably faster way is to simply
        #test both points for equality, but that can get interesting in
        #special cases... I think :/
        #PS return order matters; ClosestPoints will return (a point on self,
        #a point on other).
        pt1, pt2=self.ClosestPoints(ge)
        if not (pt1 or pt2):
            #Lines are parallel
            return None
        if pt2 in self and pt1 in ge:
            return pt1
        return None #Lines do not intersect, but may when projected.